package com.jfunit.java.mvc;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.map.HashedMap;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import com.jfunit.java.annotation.PathVariable;
import com.jfunit.java.annotation.RequestParam;
import com.jfunit.java.annotation.ResponseBody;
import com.jfunit.java.bean.Data;
import com.jfunit.java.bean.Handler;
import com.jfunit.java.bean.Params;
import com.jfunit.java.bean.Request;
import com.jfunit.java.bean.View;
import com.jfunit.java.helper.BeanHelper;
import com.jfunit.java.helper.ConfigHelper;
import com.jfunit.java.util.CastUtil;
import com.jfunit.java.util.ClassUtil;
import com.jfunit.java.util.CodeUtil;
import com.jfunit.java.util.JsonUril;
import com.jfunit.java.util.ReflectionUtil;
import com.jfunit.java.util.StreamUtil;
import com.jfunit.java.util.WebUtil;

/**
 * Handler 调用器
 * 
 * @author Administrator
 *
 */
public class HandlerInvoker {
    public static final ThreadLocal<Map<String, Object>> actionMethodParamList = new ThreadLocal<Map<String, Object>>();

    public static void invokeHandler(HttpServletRequest request, HttpServletResponse response, Handler handler)
            throws Exception {
        if (handler != null) {
            actionMethodParamList.set(new HashedMap<String, Object>());
            // 获取Controller类及其Bean实例
            Class<?> controllerClass = handler.getControllerClass();
            Method actionMethod = handler.getActionMethod();
            Object controllerBean = BeanHelper.getBean(controllerClass);
            // 创建action方法的参数列表
            createActionMethodParamList(request, handler);
            // 解析json数据为普通param变量
            getParamsFromJson(request, handler, actionMethod);
            // 定义方法的参数列表
            List<Object> paramList = getMethodParamList(request, handler);
            // 检查参数列表是否合法
            checkParamList(actionMethod, paramList);
            // 将普通参数与json参数放到自定义parameter中
            DataContext.setNewParameterMap(actionMethodParamList.get());
            // 调用 Action 方法
            Object actionMethodResult = invokeActionMethod(actionMethod, controllerBean, paramList);
            actionMethodParamList.remove();
            // 处理Action方法的返回值
            handerModelAndView(request, response, actionMethodResult);
        }
    }

    // 设置方法反射调用的参数列表
    private static List<Object> getMethodParamList(HttpServletRequest request, Handler handler) {
        List<Object> paramList = new ArrayList<Object>();
        // 获取action方法的参数类型
        Parameter[] Parameters = handler.getActionMethod().getParameters();
        // Map<Integer, Object> PathVariableParam = handler.getPathVariableParam();
        Map<String, Object> methodParamList = actionMethodParamList.get();

        for (int i = 0; i < Parameters.length; i++) {
            Class<?> paramType = Parameters[i].getType();
            if (Parameters[i].isAnnotationPresent(PathVariable.class)) {
                PathVariable pathVariable = Parameters[i].getAnnotation(PathVariable.class);
                String paramName = pathVariable.value();
                // 获取请求参数
                Object param = methodParamList.get(paramName);
                // 获取参数类型（支持四种类型:int/Integer,long/Long,double/Double,String）
                if (ClassUtil.isInt(paramType)) {
                    paramList.add(CastUtil.castInt(param));
                } else if (ClassUtil.isLong(paramType)) {
                    paramList.add(CastUtil.castLong(param));
                } else if (ClassUtil.isDouble(paramType)) {
                    paramList.add(CastUtil.castDouble(param));
                } else if (ClassUtil.isString(paramType)) {
                    paramList.add(param);
                }
            } else if (Parameters[i].isAnnotationPresent(RequestParam.class)) {
                RequestParam requestParam = Parameters[i].getAnnotation(RequestParam.class);
                String paramName = requestParam.value();
                String defaultValue = requestParam.defaultValue();
                // 获取请求参数
                Object param = methodParamList.get(paramName);
                // 获取参数类型（支持四种类型:int/Integer,long/Long,double/Double,String）
                if (ClassUtil.isInt(paramType)) {
                    paramList.add(CastUtil.castInt(param, Integer.parseInt(defaultValue)));
                } else if (ClassUtil.isLong(paramType)) {
                    paramList.add(CastUtil.castLong(param, Long.parseLong(defaultValue)));
                } else if (ClassUtil.isDouble(paramType)) {
                    paramList.add(CastUtil.castDouble(param, Double.parseDouble(defaultValue)));
                } else if (ClassUtil.isString(paramType)) {
                    paramList.add(CastUtil.castString(param, defaultValue));
                }

            }
        }

        return paramList;
    }

    private static void createActionMethodParamList(HttpServletRequest request, Handler handler)
            throws Exception {
        // 添加路径参数列表（请求路径中的带占位符参数）
        createPathParamList(handler);
        // 添加普通请求参数列表（包括 Query String 与 Form Data）
        Map<String, Object> requestParamMap = WebUtil.getRequestParamMap(request);
        if (MapUtils.isNotEmpty(requestParamMap)) {
            actionMethodParamList.get().putAll(requestParamMap);
        }
    }

    private static void createPathParamList(Handler handler) throws Exception {
        // 定义参数列表
        Map<Integer, Object> PathVariableParam = handler.getPathVariableParam();
        if (MapUtils.isNotEmpty(PathVariableParam)) {
            // 遍历正则表达式中所有匹配的组
            for (int i = 1; i <= handler.getRequestPathMatcher().groupCount(); i++) {
                // 获取请求参数
                String param = handler.getRequestPathMatcher().group(i);
                actionMethodParamList.get().put((String) PathVariableParam.get(i), param);
            }
        }

    }

    /**
     * 解析json数据为普通param变量
     * 
     * @param request
     * @param handler
     * @param actionMethod
     * @throws IOException
     */
    private static void getParamsFromJson(HttpServletRequest request, Handler handler, Method actionMethod)
            throws IOException {
        if (actionMethod.isAnnotationPresent(ResponseBody.class)) {
            String json = IOUtils.toString(request.getInputStream());
            Map<String, Object> jsonMap = JsonUril.fromJson(json, Map.class);
            actionMethodParamList.get().putAll(jsonMap);
        }
    }

    private static void checkParamList(Method actionMethod, List<Object> paramList) {
        // 判断Action方法参数个数是否匹配
        Class<?>[] actionMethodParameterTypes = actionMethod.getParameterTypes();
        if (actionMethodParameterTypes.length != paramList.size()) {
            String message = String.format("因为参数个数不匹配，所以无法调用 Action 方法！原始参数个数：%d，实际参数个数：%d",
                    actionMethodParameterTypes.length, paramList.size());
            throw new RuntimeException(message);
        }
    }

    private static Object invokeActionMethod(Method actionMethod, Object controllerBean,
            List<Object> paramList) throws IllegalAccessException, InvocationTargetException {
        Object result;
        if (CollectionUtils.isEmpty(paramList)) {
            result = ReflectionUtil.invokeMethod(controllerBean, actionMethod);
        } else {
            result = ReflectionUtil.invokeMethod(controllerBean, actionMethod, paramList.toArray());
        }
        return result;
    }

    /**
     * 处理返回的结果。决定是跳转页面 还是 返回json数据
     * 
     * @param request
     * @param response
     * @param result
     * @throws IOException
     * @throws ServletException
     */
    private static void handerModelAndView(HttpServletRequest request, HttpServletResponse response,
            Object result) throws IOException, ServletException {
        if (result instanceof View) {
            // 返回JSP页面
            View view = (View) result;
            String path = view.getPath();
            if (StringUtils.isNoneEmpty(path)) {
                if (path.endsWith("/")) {
                    response.sendRedirect(request.getContextPath() + path);
                } else {
                    Map<String, Object> model = view.getModel();
                    for (Map.Entry<String, Object> entry : model.entrySet()) {
                        request.setAttribute(entry.getKey(), entry.getValue());
                    }
                    request.getRequestDispatcher(ConfigHelper.getAppJspPath() + path).forward(request,
                            response);
                }
            }
        } else if (result instanceof Data) {
            // 返回JSON值
            Data data = (Data) result;
            Object model = data.getModel();
            if (model != null) {
                response.setContentType("application/json");
                response.setCharacterEncoding("UTF-8");
                PrintWriter writer = response.getWriter();
                String json = JsonUril.toJson(model);
                writer.write(json);
                writer.flush();
                writer.close();
            }
        }
    }
}
